package com.jadows.tdog.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSONObject;
import com.jadows.tdog.domain.ChatMessage;
import com.jadows.tdog.domain.Contact;
import com.jadows.tdog.exception.ServiceException;
import com.jadows.tdog.lock.RedisLock;
import com.jadows.tdog.optional.annotations.OpInject;
import com.jadows.tdog.po.RecentContactPo;
import com.jadows.tdog.properties.ChatMessageProperties;
import com.jadows.tdog.push.IPushService;
import com.jadows.tdog.push.dto.MessageDTO;
import com.jadows.tdog.repository.ChatMessageRepository;
import com.jadows.tdog.repository.ContactRepository;
import com.jadows.tdog.service.ChatMessageService;
import com.jadows.tdog.service.ContactService;
import com.jadows.tdog.util.SecurityUtil;
import com.jadows.tdog.util.UnionUtil;
import com.jadows.tdog.vo.RecentContactVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.jadows.tdog.enums.ServiceExceptionEnum.CONTACT_EXIT;
import static com.jadows.tdog.enums.ServiceExceptionEnum.CONTACT_NOT_EXIST;


/**
 * @author taojiacheng
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ContactServiceImpl implements ContactService {

    final ContactRepository contactRepository;
    final ChatMessageRepository chatMessageRepository;
    final MongoTemplate mongoTemplate;
    final RedissonClient redissonClient;
    final ChatMessageService chatMessageService;
    final ChatMessageProperties chatMessageProperties;

    @OpInject
    IPushService pushService;

    @Override
    public List<RecentContactVo> listRecentContacts() {
        // 获取当前用户的最近联系人列表
        String memberId = SecurityUtil.getCurrentUser().getMemberId();
        List<Contact> contacts = contactRepository.findContactsByMemberId(mongoTemplate, memberId);
        if (CollUtil.isEmpty(contacts)) {
            return Collections.emptyList();
        }
        List<String> unionIds = contacts.stream().map(Contact::getUnionId).collect(Collectors.toList());
        Map<String, ChatMessageRepository.ChatMessageGroup> last10MessagesGroup = chatMessageService.findLast10Messages(unionIds);
        List<RecentContactVo> recentContactVos = new ArrayList<>();
        for (Contact contact : contacts) {
            RecentContactVo recentContactVo = contactToRecentContactVo(contact);
            ChatMessageRepository.ChatMessageGroup chatMessageGroup = last10MessagesGroup.get(contact.getUnionId());
            if (chatMessageGroup == null) {
                recentContactVo.setUnreadCount(0);
                recentContactVo.setLast10Messages(Collections.emptyList());
                recentContactVos.add(recentContactVo);
                continue;
            }
            // 未读消息数
            recentContactVo.setUnreadCount(chatMessageGroup.getUnreadCount());
            // 最近10条消息
            List<ChatMessage> messages = chatMessageGroup.getMessages();
            Contact.ContactInfo currentContact = contact.getContactInfoByUid(memberId);
            assert currentContact != null;
            Long lastMessageIdBeforeDeleted = currentContact.getLastMessageIdBeforeDeleted();
            if (lastMessageIdBeforeDeleted != null) {
                log.info("lastMessageIdBeforeDeleted:{}, uid: {}", lastMessageIdBeforeDeleted, currentContact.getUid());
                // 消息id 递增
                messages.removeIf(message -> message.getMessageId() <= lastMessageIdBeforeDeleted);
            }
            if (CollUtil.isEmpty(messages)) {
                // 无最近聊天记录的联系人不展示
                continue;
            }
            recentContactVo.setLast10Messages(messages);
            recentContactVos.add(recentContactVo);
        }
        return sortRecentContacts(recentContactVos);
    }

    /**
     * 按照最近一条消息的发送时间给联系人排序
     * 没有发送过消息的联系人排在最后
     */
    private List<RecentContactVo> sortRecentContacts(List<RecentContactVo> result) {
        return result.stream().sorted((o1, o2) -> {
            List<ChatMessage> last10Messages1 = o1.getLast10Messages();
            List<ChatMessage> last10Messages2 = o2.getLast10Messages();
            if (CollUtil.isEmpty(last10Messages1) && CollUtil.isEmpty(last10Messages2)) {
                return 0;
            }
            if (CollUtil.isEmpty(last10Messages1)) {
                return 1;
            }
            if (CollUtil.isEmpty(last10Messages2)) {
                return -1;
            }
            // sendTime 是按照由远到近排序的
            ChatMessage lastMessage1 = last10Messages1.get(last10Messages1.size() - 1);
            ChatMessage lastMessage2 = last10Messages2.get(last10Messages2.size() - 1);
            return lastMessage2.getSendTime().compareTo(lastMessage1.getSendTime());
        }).collect(Collectors.toList());
    }


    @Override
    @RedisLock(lockName = "upd_contact", key = "#recentContactPo.unionId", expire = 100)
    public RecentContactVo createRecentContactIfAbsent(RecentContactPo recentContactPo) {
        // 当前用户的memberId
        String memberId = SecurityUtil.getCurrentUser().getMemberId();
        // 目标用户的id
        String targetId = recentContactPo.getTargetId();
        // 查询当前用户和目标用户是否存在最近联系人关系
        Contact contact = findContactByUnionId(UnionUtil.getUnionId(memberId, targetId));
        if (contact != null) {
            // 已经存在联系人关系，获取最近10条消息并返回
            RecentContactVo recentContactVo = contactToRecentContactVo(contact);
            // 更新联系人信息
            contact = updateRecentContact(contact);
            Map<String, ChatMessageRepository.ChatMessageGroup> last10MessagesGroup = chatMessageService.findLast10Messages(CollUtil.toList(contact.getUnionId()));
            ChatMessageRepository.ChatMessageGroup chatMessageGroup = last10MessagesGroup.get(contact.getUnionId());
            if (chatMessageGroup == null) {
                // 和新的联系人聊天，无最近聊天记录
                recentContactVo.setUnreadCount(0);
                recentContactVo.setLast10Messages(Collections.emptyList());
                return recentContactVo;
            }
            List<ChatMessage> chatMessages = chatMessageGroup.getMessages();
            Contact.ContactInfo contactInfo = contact.getContactInfoByUid(memberId);
            assert contactInfo != null;
            Long lastMessageIdBeforeDeleted = contactInfo.getLastMessageIdBeforeDeleted();
            if (lastMessageIdBeforeDeleted != null) {
                // 消息id 递增
                log.info("lastMessageIdBeforeDeleted:{}", lastMessageIdBeforeDeleted);
                chatMessages.removeIf(message -> message.getMessageId() <= lastMessageIdBeforeDeleted);
            }
            recentContactVo.setUnreadCount(chatMessageGroup.getUnreadCount());
            recentContactVo.setLast10Messages(chatMessages);
            return recentContactVo;
        }
        // 创建联系人关系
        contact = new Contact();
        Contact.ContactInfo from = new Contact.ContactInfo();
        Contact.ContactInfo to = new Contact.ContactInfo();
        contact.setFromUser(from);
        contact.setToUser(to);
        // 保存联系人信息
        contact = createRecentContact(recentContactPo, contact);
        return contactToRecentContactVo(contact);
    }


    /**
     * 更新最近联系人信息
     *
     * @param recentContactPo 联系人数据
     */
    @Override
    @RedisLock(lockName = "upd_contact", key = "#recentContactPo.unionId", expire = 100)
    public void updateRecentContact(RecentContactPo recentContactPo) {
        String memberId = SecurityUtil.getCurrentUser().getMemberId();
        String targetId = recentContactPo.getTargetId();
        Contact contact = findContactByUnionId(UnionUtil.getUnionId(memberId, targetId));
        if (contact == null) {
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        this.updateRecentContact(contact);
    }

    @Override
    public List<RecentContactVo> listBlockContacts() {
        String memberId = SecurityUtil.getCurrentUser().getMemberId();
        List<Contact> blockContacts = contactRepository.findBlockContactsByMemberId(mongoTemplate, memberId);
        if (CollUtil.isEmpty(blockContacts)) {
            return Collections.emptyList();
        }
        return blockContacts.stream().map(this::contactToRecentContactVo).collect(Collectors.toList());
    }


    @Override
    public Integer recentOut() {
        String memberId = SecurityUtil.getCurrentUser().getMemberId();
        List<Contact> contacts = contactRepository.removeContactByMemberId(mongoTemplate, memberId);
        if (CollUtil.isEmpty(contacts)) {
            return 0;
        }
        return contacts.size();
    }


    /**
     * 更新最近联系人信息
     */
    protected Contact updateRecentContact(Contact contact) {
        Contact.ContactInfo fromUser = contact.getFromUser();
        String fromUid = fromUser.getUid();
        contact.setUpdateTime(new Date());
        return contactRepository.save(contact);
    }

    /**
     * 更新最近联系人信息
     */
    protected Contact createRecentContact(RecentContactPo recentContactPo, Contact contact) {
        // 当前用户的memberId
        String memberId = SecurityUtil.getCurrentUser().getMemberId();
        // 目标用户的id
        String targetId = recentContactPo.getTargetId();

        if (isOutMember(targetId)) {
            throw new ServiceException(CONTACT_EXIT);
        }

        // 更新最近联系人信息
        Contact.ContactInfo from = contact.getFromUser();
        from.setUid(memberId);

        Contact.ContactInfo toUser = contact.getToUser();
        toUser.setUid(targetId);

        // 当前用户信息
        // unionId 取值为两个用户的memberId拼接
        String unionId = UnionUtil.getUnionId(memberId, targetId);
        contact.setUnionId(unionId);

        contact.setCreateTime(new Date());
        contact.setUpdateTime(new Date());
        return contactRepository.save(contact);
    }


    @Override
    public Contact findContactByUnionId(String unionId) {
        return contactRepository.findContactByUnionId(unionId);
    }

    @Override
    public Long pushMessage(String fromUid, String toUid, String title, String body) {
        // 获取联系人
        String unionId = UnionUtil.getUnionId(fromUid, toUid);
        Contact contact = findContactByUnionId(unionId);
        if (contact == null) {
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        Contact.ContactInfo toUser = contact.getContactInfoByUid(toUid);
        if (toUser == null) {
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        if (StringUtils.isBlank(toUser.getFcmToken())) {
            log.warn("toUser 不存在 fcmToken toUid:{}", toUid);
            return 0L;
        }
        // fromUser
        Contact.ContactInfo fromUser = contact.getContactInfoByUid(fromUid);
        if (fromUser == null) {
            log.error("fromUser 不存在 fromUid:{}", fromUid);
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        MessageDTO messageDTO = MessageDTO.builder()
                .fcmToken(toUser.getFcmToken())
                .registrationId(toUser.getRegistrationId())
                .title(fromUser.getNickname())
                .body(body)
                .pushType(chatMessageProperties.getPushType())
                .fromUid(fromUid)
                .fromAvatar(fromUser.getAvatar())
                .toUid(toUid)
                .build();
        JSONObject ignored = pushService.pushMessage(messageDTO);
        return 1L;
    }

    @Override
    public void blockRecentContact(RecentContactPo recentContactPo) {
        String currentMemberId = SecurityUtil.getCurrentUser().getMemberId();
        String unionId = UnionUtil.getUnionId(currentMemberId, recentContactPo.getTargetId());
        Contact contact = findContactByUnionId(unionId);
        if (contact == null) {
            log.warn("block. 联系人不存在 unionId:{}", unionId);
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        Contact.ContactInfo contactInfo = contact.getContactInfoByUid(currentMemberId);
        if (contactInfo == null) {
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        contactInfo.setBlocked(true);
        contactInfo.setBlockTime(new Date());
        contactRepository.save(contact);
    }

    @Override
    public void unblockRecentContact(RecentContactPo recentContactPo) {
        String unionId = UnionUtil.getUnionId(SecurityUtil.getCurrentUser()
                .getMemberId(), recentContactPo.getTargetId());
        Contact contact = findContactByUnionId(unionId);
        if (contact == null) {
            log.warn("unblock. 联系人不存在 unionId:{}", unionId);
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        Contact.ContactInfo contactInfo = contact.getContactInfoByUid(SecurityUtil.getCurrentUser().getMemberId());
        if (contactInfo == null) {
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        contactInfo.setBlocked(false);
        contactRepository.save(contact);
    }

    /**
     * 用户A & 用户B
     * A 删除了B, 则 A 联系人列表看不到B, A 再次收到 B 的   消息时, 只展示新的聊天记录
     */
    @Override
    public void deleteRecentContact(RecentContactPo recentContactPo) {
        String unionId = UnionUtil.getUnionId(SecurityUtil.getCurrentUser()
                .getMemberId(), recentContactPo.getTargetId());
        Contact contact = findContactByUnionId(unionId);
        if (contact == null) {
            log.warn("delete. 联系人不存在 unionId:{}", unionId);
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        Contact.ContactInfo contactInfo = contact.getContactInfoByUid(SecurityUtil.getCurrentUser().getMemberId());
        if (contactInfo == null) {
            throw new ServiceException(CONTACT_NOT_EXIST);
        }
        // 记录最后一条聊天记录的Id
        ChatMessage chatMessage = chatMessageRepository.findFirstByUnionIdOrderByMessageIdDesc(unionId);
        if (chatMessage != null) {
            log.info("delete. 联系人存在聊天记录 cMemberId:{}, memberId:{}, messageId:{}, content: {}",
                    SecurityUtil.getCurrentUser()
                            .getMemberId(), contactInfo.getUid(), chatMessage.getMessageId(), chatMessage.getContent());
            contactInfo.setLastMessageIdBeforeDeleted(chatMessage.getMessageId());
            // 更新被删除的联系人的未读消息数
            chatMessageRepository.updateIsReadWhenDeletedContact(mongoTemplate, unionId,
                    recentContactPo.getTargetId(), chatMessage.getMessageId());

        }
        contactRepository.save(contact);
    }


    /**
     * 用户是否注销
     */
    public Boolean isOutMember(String memberId) {
        return false;
    }


    /**
     * contact -> recentContactVo
     */
    public RecentContactVo contactToRecentContactVo(Contact contact) {
        RecentContactVo recentContactVo = new RecentContactVo();
        Contact.ContactInfo otherContact = contact.getOtherContactInfoByUid(SecurityUtil.getCurrentUser()
                .getMemberId());
        assert otherContact != null;
        recentContactVo.setAvatar(otherContact.getAvatar());
        if (StringUtils.isBlank(recentContactVo.getAvatar())) {
            recentContactVo.setAvatar("https://img.js.design/assets/img/66162695787029539a75f75f.png#9218f925e28fafe038bcfa56b25b7075");
        }
        recentContactVo.setNickname(otherContact.getNickname());
        recentContactVo.setMemberId(otherContact.getUid());
        recentContactVo.setBlockByOther(otherContact.getBlocked());
        recentContactVo.setBlockByOtherTime(otherContact.getBlockTime());
        recentContactVo.setBgColor(otherContact.getBgColor());

        // 当前用户
        Contact.ContactInfo currentContact = contact.getContactInfoByUid(SecurityUtil.getCurrentUser().getMemberId());
        assert currentContact != null;
        recentContactVo.setBlockByMe(currentContact.getBlocked());
        recentContactVo.setBlockByMeTime(currentContact.getBlockTime());

        if (Objects.equals(otherContact.getStatus(), 2)) {
            // 联系人已删除
            recentContactVo.setStatus(2);
            recentContactVo.setNickname("已注销用户");
            recentContactVo.setAvatar("https://fated-img.s3.ap-northeast-2.amazonaws.com/profile/noimg_thumb.png");
        }
        return recentContactVo;
    }

}
